<template>
  <div>
    <p>常见简单几何体</p>
    <div class="threejs_box"></div>
  </div>
</template>

<script setup>
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { nextTick } from "vue";
nextTick(() => {
  // 场景
  const scene = new THREE.Scene();
  // 材质
  const material = new THREE.MeshPhongMaterial({
    color: 0x00ffa0,
    shininess: 15,
    specular: 0xffffff,
  });
  // 几何体
  /**
   * 矩形
   *width — X 轴上面的宽度，默认值为 1。
    height — Y 轴上面的高度，默认值为 1。
    depth — Z 轴上面的深度，默认值为 1。
    widthSegments — （可选）宽度的分段数，默认值是 1。
    heightSegments — （可选）高度的分段数，默认值是 1。
    depthSegments — （可选）深度的分段数，默认值是 1。
   * */
  const boxGeo = new THREE.BoxGeometry(50, 50, 50); //矩形
  const edges = new THREE.EdgesGeometry(boxGeo);
  const boxline = new THREE.LineSegments(
    edges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  scene.add(boxline);
  /**
   * 胶囊
   * radius — 胶囊半径。可选的; 默认值为1。
    length — 中间区域的长度。可选的; 默认值为1。
    capSegments — 构造盖子的曲线部分的个数。可选的; 默认值为4。
    radialSegments — 覆盖胶囊圆周的分离的面的个数。可选的; 默认值为8。
   * */
  const capsuleGeo = new THREE.CapsuleGeometry(25, 25, 10, 10);

  /**
   * 圆形缓冲几何体
   * radius — 圆形的半径，默认值为1
    segments — 分段（三角面）的数量，最小值为3，默认值为32。
    thetaStart — 第一个分段的起始角度，默认为0。（three o'clock position）
    thetaLength — 圆形扇区的中心角，通常被称为“θ”（西塔）。默认值是2*Pi，这使其成为一个完整的圆。
   * */
  const circleGeo = new THREE.CircleGeometry(25, 30);
  /**
   * 圆锥
   * radius — 圆锥底部的半径，默认值为1。
    height — 圆锥的高度，默认值为1。
    radialSegments — 圆锥侧面周围的分段数，默认为32。
    heightSegments — 圆锥侧面沿着其高度的分段数，默认值为1。
    openEnded — 一个Boolean值，指明该圆锥的底面是开放的还是封顶的。默认值为false，即其底面默认是封顶的。
    thetaStart — 第一个分段的起始角度，默认为0。（three o'clock position）
    thetaLength — 圆锥底面圆扇区的中心角，通常被称为“θ”（西塔）。默认值是2*Pi，这使其成为一个完整的圆锥。
    * */
  const coneGeo = new THREE.ConeGeometry(25, 50, 64);
  /**
   * 圆柱
   * radiusTop — 圆柱的顶部半径，默认值是1。
    radiusBottom — 圆柱的底部半径，默认值是1。
    height — 圆柱的高度，默认值是1。
    radialSegments — 圆柱侧面周围的分段数，默认为32。
    heightSegments — 圆柱侧面沿着其高度的分段数，默认值为1。
    openEnded — 一个Boolean值，指明该圆锥的底面是开放的还是封顶的。默认值为false，即其底面默认是封顶的。
    thetaStart — 第一个分段的起始角度，默认为0。（three o'clock position）
    thetaLength — 圆柱底面圆扇区的中心角，通常被称为“θ”（西塔）。默认值是2*Pi，这使其成为一个完整的圆柱。
   * */
  const cylinderGeo = new THREE.CylinderGeometry(15, 25, 50);
  /**
   * 十二面几何体
   * radius — 十二面体的半径，默认值为1。
    detail — 默认值为0。将这个值设为一个大于0的数将会为它增加一些顶点，使其不再是一个十二面体。
   * */
  const dodeGeo = new THREE.DodecahedronGeometry(25, 0); //十四面

  //  物体对象*********************************************************
  // 矩形
  const boxMesh = new THREE.Mesh(boxGeo, material);
  boxMesh.position.set(0, 0, 0);
  scene.add(boxMesh);
  // 胶囊
  const capsuleMesh = new THREE.Mesh(capsuleGeo, material);
  capsuleMesh.position.set(100, 0, 0);
  scene.add(capsuleMesh);
  const capsuleEdges = new THREE.EdgesGeometry(capsuleGeo);
  const capsuleline = new THREE.LineSegments(
    capsuleEdges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  capsuleline.position.set(100, 0, 0);
  scene.add(capsuleline);
  // 圆形缓冲几何体
  const circleMesh = new THREE.Mesh(circleGeo, material);
  circleMesh.position.set(170, 0, 0);
  scene.add(circleMesh);
  const circleEdges = new THREE.EdgesGeometry(circleGeo);
  const circleline = new THREE.LineSegments(
    circleEdges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  circleline.position.set(170, 0, 0);
  scene.add(circleline);
  // 圆锥
  const coneMesh = new THREE.Mesh(coneGeo, material);
  coneMesh.position.set(240, 0, 0);
  scene.add(coneMesh);
  let coneEdges = new THREE.EdgesGeometry(coneGeo);
  const coneLine = new THREE.LineSegments(
    coneEdges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  coneLine.position.set(240, 0, 0);
  scene.add(coneLine);
  // 圆柱
  const cylinderMesh = new THREE.Mesh(cylinderGeo, material);
  cylinderMesh.position.set(300, 0, 0);
  scene.add(cylinderMesh);
  const cylinderEdges = new THREE.EdgesGeometry(cylinderGeo);
  const cylinderLine = new THREE.LineSegments(
    cylinderEdges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  cylinderLine.position.set(300, 0, 0);
  scene.add(cylinderLine);
  // 十二面
  const dodeMesh = new THREE.Mesh(dodeGeo, material);
  dodeMesh.position.set(370, 0, 0);
  scene.add(dodeMesh);
  const dodeEdges = new THREE.EdgesGeometry(dodeGeo);
  const dodeLine = new THREE.LineSegments(
    dodeEdges,
    new THREE.LineBasicMaterial({ color: 0xffffff })
  );
  dodeLine.position.set(370, 0, 0);
  scene.add(dodeLine);
  // **************************************************************
  //  坐标
  let xyz = new THREE.AxesHelper(100);
  scene.add(xyz);
  //   DOM
  let Dom = document.querySelector(".threejs_box");
  let width = Dom.offsetWidth;
  let height = Dom.offsetHeight;
  //  照相机
  const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 3000);
  camera.position.set(700, 40, 150);
  camera.lookAt(370, 0, 0);
  //   光源
  const amLight = new THREE.AmbientLight(0xffffff, 1.0);
  scene.add(amLight);
  // 点光源
  const pointLight = new THREE.PointLight(0xffffff, 1);
  pointLight.position.set(200, 200, 200);
  scene.add(pointLight);
  let pointLightP = new THREE.PointLightHelper(pointLight, 5);
  scene.add(pointLightP);
  // 平行光
  const dirLight = new THREE.DirectionalLight(0xffffff, 1.0);
  // 平行光的出发点
  dirLight.position.set(100, 70, 50);
  scene.add(dirLight);
  // 平行光  可视化
  const dirLightHelper = new THREE.DirectionalLightHelper(
    dirLight,
    10,
    0xff0000
  );
  scene.add(dirLightHelper);
  //   渲染器
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height);
  renderer.render(scene, camera);
  Dom.appendChild(renderer.domElement);
  //   相机控件
  let controls = new OrbitControls(camera, renderer.domElement);
  controls.addEventListener("change", function () {
    renderer.render(scene, camera);
  });
  // 旋转动画
  // ********************* 动画 *** 渲染循环 *********************************
  /**
   * 旋转动画
   * */
  function rotate() {
    boxGeo.rotateY(0.005);
    boxline.rotateY(0.005);
    capsuleGeo.rotateX(0.005);
    capsuleline.rotateX(0.005);
    // circleGeo.rotateX(0.05);
    coneGeo.rotateX(0.01);
    coneLine.rotateX(0.01);
    cylinderGeo.rotateX(0.005);
    cylinderLine.rotateX(0.005);
    dodeGeo.rotateY(0.005);
    dodeLine.rotateY(0.005);
    renderer.render(scene, camera);
    // requestAnimationFrame是window的一个API，实现周期性循环执行，
    // 默认每秒钟执行60次，但是不一定做得到，要看代码的性能
    requestAnimationFrame(rotate);
  }
  rotate();
});
</script>


<style scoped>
.threejs_box {
  width: 70vw;
  height: 80vh;
}
</style>